import json
import os
import platform
import socket
import subprocess
import threading
import time
import tkinter as tk
from datetime import datetime
from tkinter import ttk, filedialog, messagebox, scrolledtext

from ttkbootstrap.constants import *

from app.util.annotation_util import annotate, ClassInfo
from app.view.base_view import View

# 常量定义
DEFAULT_PORT = 14330
BUFFER_SIZE = 4096
DISCOVERY_PORT = 14331
BROADCAST_INTERVAL = 5  # 设备发现广播间隔(秒)
FILE_CHUNK_SIZE = 4096  # 文件传输块大小
MAX_LOG_LINES = 1000  # 最大日志行数

# 消息类型常量
MSG_DISCOVER = "DISCOVER"  # 发现设备请求
MSG_RESPONSE = "RESPONSE"  # 发现设备响应
MSG_FILE_LIST = "FILE_LIST"  # 请求文件列表
MSG_FILE_DATA = "FILE_DATA"  # 文件数据
MSG_DOWNLOAD = "DOWNLOAD"  # 下载请求
MSG_UPLOAD = "UPLOAD"  # 上传请求
MSG_SUCCESS = "SUCCESS"  # 操作成功
MSG_ERROR = "ERROR"  # 操作错误


@annotate(ClassInfo(name="网络文件传输", category="tool"))
class TcpFileTransferView(View):
    """设置视图"""

    def __init__(self, parent, controller):
        super().__init__(parent, controller)
        # 初始化数据库
        # 状态变量
        self.server_running = False
        self.server_socket = None
        self.transfer_port = DEFAULT_PORT
        self.shared_folder = os.path.expanduser("~/共享文件夹")
        self.connected_device = None
        self.devices = {}  # 存储发现的设备 {ip: (name, port, last_seen)}

        # 创建共享文件夹
        if not os.path.exists(self.shared_folder):
            os.makedirs(self.shared_folder)

        # 创建UI
        self._create_widgets()

        # 启动服务
        self.start_server()

        # 启动设备发现
        self.start_discovery()

        # 窗口关闭事件
        # self.protocol("WM_DELETE_WINDOW", self.on_close)

    def _create_widgets(self):
        # 顶部控制区域
        top_frame = ttk.LabelFrame(self, text="服务控制", padding=10)
        top_frame.pack(fill=X, padx=10, pady=5)

        # 端口设置
        port_frame = ttk.Frame(top_frame)
        port_frame.pack(side=LEFT, padx=10)
        ttk.Label(port_frame, text="端口:").pack(side=LEFT, padx=5)
        self.port_entry = ttk.Entry(port_frame, width=10)
        self.port_entry.pack(side=LEFT, padx=5)
        self.port_entry.insert(0, str(DEFAULT_PORT))

        # 共享文件夹设置
        folder_frame = ttk.Frame(top_frame)
        folder_frame.pack(side=LEFT, padx=10, fill=X, expand=True)
        ttk.Label(folder_frame, text="共享文件夹:").pack(side=LEFT, padx=5)
        self.folder_entry = ttk.Entry(folder_frame)
        self.folder_entry.pack(side=LEFT, fill=X, expand=True, padx=5)
        self.folder_entry.insert(0, self.shared_folder)

        self.browse_btn = ttk.Button(folder_frame, text="浏览...", command=self.browse_folder, bootstyle=INFO)
        self.browse_btn.pack(side=LEFT, padx=5)

        # 服务控制按钮
        control_frame = ttk.Frame(top_frame)
        control_frame.pack(side=RIGHT, padx=10)
        self.start_btn = ttk.Button(control_frame, text="启动服务", command=self.start_server, bootstyle=SUCCESS)
        self.start_btn.pack(side=LEFT, padx=5)
        self.stop_btn = ttk.Button(control_frame, text="停止服务", command=self.stop_server, bootstyle=DANGER)
        self.stop_btn.pack(side=LEFT, padx=5)
        self.stop_btn.config(state=DISABLED)

        # 主内容区域
        main_frame = ttk.Frame(self, padding=10)
        main_frame.pack(fill=BOTH, expand=True)

        # 左侧设备列表
        device_frame = ttk.LabelFrame(main_frame, text="局域网设备", padding=10)
        device_frame.pack(side=LEFT, fill=BOTH, expand=True, padx=(0, 5))

        self.device_listbox = ttk.Treeview(device_frame, columns=("name", "ip", "last_seen"), show="headings")
        self.device_listbox.heading("name", text="设备名称")
        self.device_listbox.heading("ip", text="IP地址")
        self.device_listbox.heading("last_seen", text="最后在线")
        self.device_listbox.column("name", width=150)
        self.device_listbox.column("ip", width=120)
        self.device_listbox.column("last_seen", width=120)
        self.device_listbox.pack(fill=BOTH, expand=True)
        self.device_listbox.bind("<<TreeviewSelect>>", self.on_device_select)

        device_btn_frame = ttk.Frame(device_frame)
        device_btn_frame.pack(fill=X, pady=5)
        self.refresh_devices_btn = ttk.Button(device_btn_frame, text="刷新设备", command=self.refresh_devices,
                                              bootstyle=INFO)
        self.refresh_devices_btn.pack(fill=X)

        # 中间文件列表区域
        file_frame = ttk.LabelFrame(main_frame, text="文件列表", padding=10)
        file_frame.pack(side=LEFT, fill=BOTH, expand=True, padx=5)

        self.file_listbox = ttk.Treeview(file_frame, columns=("name", "size", "modified"), show="headings")
        self.file_listbox.heading("name", text="文件名")
        self.file_listbox.heading("size", text="大小")
        self.file_listbox.heading("modified", text="修改时间")
        self.file_listbox.column("name", width=250)
        self.file_listbox.column("size", width=100)
        self.file_listbox.column("modified", width=150)
        self.file_listbox.pack(fill=BOTH, expand=True)
        self.file_listbox.bind("<<TreeviewSelect>>", self.on_file_select)

        file_btn_frame = ttk.Frame(file_frame)
        file_btn_frame.pack(fill=X, pady=5)

        self.download_btn = ttk.Button(file_btn_frame, text="下载选中文件", command=self.download_file,
                                       bootstyle=SUCCESS)
        self.download_btn.pack(side=LEFT, padx=5, fill=X, expand=True)
        self.download_btn.config(state=DISABLED)

        self.preview_btn = ttk.Button(file_btn_frame, text="预览选中文件", command=self.preview_file, bootstyle=INFO)
        self.preview_btn.pack(side=LEFT, padx=5, fill=X, expand=True)
        self.preview_btn.config(state=DISABLED)

        # 右侧上传区域
        upload_frame = ttk.LabelFrame(main_frame, text="文件上传", padding=10)
        upload_frame.pack(side=LEFT, fill=BOTH, expand=True, padx=(5, 0))

        self.local_file_listbox = tk.Listbox(upload_frame)
        self.local_file_listbox.pack(fill=BOTH, expand=True)

        upload_btn_frame = ttk.Frame(upload_frame)
        upload_btn_frame.pack(fill=X, pady=5)

        self.add_file_btn = ttk.Button(upload_btn_frame, text="添加文件", command=self.add_local_files, bootstyle=INFO)
        self.add_file_btn.pack(side=LEFT, padx=5, fill=X, expand=True)

        self.remove_file_btn = ttk.Button(upload_btn_frame, text="移除选中", command=self.remove_local_file,
                                          bootstyle=WARNING)
        self.remove_file_btn.pack(side=LEFT, padx=5, fill=X, expand=True)

        self.upload_btn = ttk.Button(upload_btn_frame, text="上传选中文件", command=self.upload_files,
                                     bootstyle=SUCCESS)
        self.upload_btn.pack(side=LEFT, padx=5, fill=X, expand=True)
        self.upload_btn.config(state=DISABLED)

        # 底部状态和日志区域
        status_frame = ttk.LabelFrame(self, text="状态日志", padding=10)
        status_frame.pack(fill=BOTH, expand=True, padx=10, pady=5)

        self.log_text = scrolledtext.ScrolledText(status_frame, wrap=tk.WORD, state=DISABLED, height=5)
        self.log_text.pack(fill=BOTH, expand=True)

        self.status_var = tk.StringVar(value="就绪")
        self.status_bar = ttk.Label(self, textvariable=self.status_var, relief=SUNKEN, anchor=W)
        self.status_bar.pack(side=BOTTOM, fill=X)

    def log(self, message):
        """添加日志信息，并控制日志长度"""
        timestamp = time.strftime("%H:%M:%S")
        self.log_text.config(state=NORMAL)

        # 插入新日志
        self.log_text.insert(END, f"[{timestamp}] {message}\n")

        # 检查并限制日志行数
        line_count = int(self.log_text.index('end-1c').split('.')[0])
        if line_count > MAX_LOG_LINES:
            # 超过最大行数时保留后700行
            self.log_text.delete(1.0, f"{line_count - 700}.0")

        self.log_text.see(END)
        self.log_text.config(state=DISABLED)
        self.status_var.set(message)

    def browse_folder(self):
        """浏览选择共享文件夹"""
        folder = filedialog.askdirectory(title="选择共享文件夹")
        if folder:
            self.shared_folder = folder
            self.folder_entry.delete(0, END)
            self.folder_entry.insert(0, folder)
            self.log(f"共享文件夹已设置为: {folder}")

    def start_server(self):
        """启动文件传输服务"""
        try:
            # 获取端口号
            port = int(self.port_entry.get())
            if port < 1 or port > 65535:
                messagebox.showerror("错误", "端口号必须在1-65535之间")
                return

            self.transfer_port = port

            # 启动TCP服务器
            self.server_running = True
            self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.server_socket.bind(('0.0.0.0', port))
            self.server_socket.listen(5)

            # 更新UI状态
            self.start_btn.config(state=DISABLED)
            self.stop_btn.config(state=NORMAL)
            self.port_entry.config(state=DISABLED)

            self.log(f"文件传输服务已启动，端口: {port}")
            self.log(f"共享文件夹: {self.shared_folder}")

            # 启动服务器线程
            threading.Thread(target=self.accept_connections, daemon=True).start()

        except Exception as e:
            self.log(f"启动服务失败: {str(e)}")
            messagebox.showerror("错误", f"启动服务失败: {str(e)}")

    def stop_server(self):
        """停止文件传输服务"""
        if self.server_running:
            self.server_running = False

            # 关闭服务器socket
            if self.server_socket:
                try:
                    self.server_socket.close()
                except Exception as e:
                    self.log(f"关闭服务器失败: {str(e)}")

            # 更新UI状态
            self.start_btn.config(state=NORMAL)
            self.stop_btn.config(state=DISABLED)
            self.port_entry.config(state=NORMAL)

            self.log("文件传输服务已停止")

    def accept_connections(self):
        """接受客户端连接"""
        while self.server_running:
            try:
                client_socket, client_address = self.server_socket.accept()
                self.log(f"新连接: {client_address[0]}:{client_address[1]}")

                # 启动线程处理客户端请求
                threading.Thread(
                    target=self.handle_client,
                    args=(client_socket, client_address),
                    daemon=True
                ).start()

            except Exception as e:
                if self.server_running:
                    self.log(f"接受连接错误: {str(e)}")
                break

    def handle_client(self, client_socket, client_address):
        """处理客户端请求"""
        try:
            while self.server_running:
                # 接收客户端请求
                data = client_socket.recv(BUFFER_SIZE).decode('utf-8')
                if not data:
                    break

                # 解析请求
                request = json.loads(data)
                msg_type = request.get('type')

                if msg_type == MSG_FILE_LIST:
                    # 发送文件列表
                    file_list = self.get_file_list()
                    response = {
                        'type': MSG_FILE_LIST,
                        'data': file_list
                    }
                    client_socket.sendall(json.dumps(response).encode('utf-8'))
                    self.log(f"向 {client_address[0]} 发送文件列表")

                elif msg_type == MSG_DOWNLOAD:
                    # 处理下载请求
                    filename = request.get('data')
                    self.send_file(client_socket, filename)
                    self.log(f"向 {client_address[0]} 发送文件: {filename}")

                elif msg_type == MSG_UPLOAD:
                    # 处理上传请求
                    filename = request.get('data')
                    self.receive_file(client_socket, filename)
                    self.log(f"从 {client_address[0]} 接收文件: {filename}")

                else:
                    # 未知请求类型
                    response = {
                        'type': MSG_ERROR,
                        'data': f"未知请求类型: {msg_type}"
                    }
                    client_socket.sendall(json.dumps(response).encode('utf-8'))

        except Exception as e:
            self.log(f"与 {client_address[0]} 通信错误: {str(e)}")
        finally:
            try:
                client_socket.close()
            except:
                pass
            self.log(f"与 {client_address[0]} 的连接已关闭")

    def get_file_list(self):
        """获取共享文件夹中的文件列表"""
        file_list = []
        try:
            for filename in os.listdir(self.shared_folder):
                file_path = os.path.join(self.shared_folder, filename)
                if os.path.isfile(file_path):
                    file_size = os.path.getsize(file_path)
                    modified_time = datetime.fromtimestamp(os.path.getmtime(file_path)).strftime('%Y-%m-%d %H:%M')
                    file_list.append({
                        'name': filename,
                        'size': self.format_file_size(file_size),
                        'raw_size': file_size,
                        'modified': modified_time
                    })
        except Exception as e:
            self.log(f"获取文件列表错误: {str(e)}")

        return file_list

    def format_file_size(self, size_bytes):
        """格式化文件大小显示"""
        if size_bytes < 1024:
            return f"{size_bytes} B"
        elif size_bytes < 1024 * 1024:
            return f"{size_bytes / 1024:.2f} KB"
        elif size_bytes < 1024 * 1024 * 1024:
            return f"{size_bytes / (1024 * 1024):.2f} MB"
        else:
            return f"{size_bytes / (1024 * 1024 * 1024):.2f} GB"

    def send_file(self, client_socket, filename):
        """发送文件给客户端"""
        try:
            file_path = os.path.join(self.shared_folder, filename)

            # 检查文件是否存在
            if not os.path.exists(file_path) or not os.path.isfile(file_path):
                response = {
                    'type': MSG_ERROR,
                    'data': f"文件不存在: {filename}"
                }
                client_socket.sendall(json.dumps(response).encode('utf-8'))
                return

            # 发送文件信息
            file_size = os.path.getsize(file_path)
            file_info = {
                'type': MSG_FILE_DATA,
                'data': {
                    'name': filename,
                    'size': file_size
                }
            }
            client_socket.sendall(json.dumps(file_info).encode('utf-8'))

            # 等待客户端确认
            confirm = client_socket.recv(BUFFER_SIZE).decode('utf-8')
            if not confirm or json.loads(confirm).get('type') != MSG_SUCCESS:
                self.log("客户端未确认接收文件")
                return

            # 发送文件内容
            with open(file_path, 'rb') as f:
                while True:
                    data = f.read(FILE_CHUNK_SIZE)
                    if not data:
                        break
                    client_socket.sendall(data)

            # 等待客户端接收完成确认
            confirm = client_socket.recv(BUFFER_SIZE).decode('utf-8')
            if confirm and json.loads(confirm).get('type') == MSG_SUCCESS:
                self.log(f"文件 {filename} 发送成功")
            else:
                self.log(f"文件 {filename} 发送失败")

        except Exception as e:
            self.log(f"发送文件错误: {str(e)}")
            response = {
                'type': MSG_ERROR,
                'data': str(e)
            }
            client_socket.sendall(json.dumps(response).encode('utf-8'))

    def receive_file(self, client_socket, filename):
        """接收客户端发送的文件"""
        try:
            file_path = os.path.join(self.shared_folder, filename)

            # 发送确认
            confirm = {
                'type': MSG_SUCCESS,
                'data': "准备接收文件"
            }
            client_socket.sendall(json.dumps(confirm).encode('utf-8'))

            # 接收文件信息
            file_info_data = client_socket.recv(BUFFER_SIZE).decode('utf-8')
            if not file_info_data:
                self.log("未收到文件信息")
                return

            file_info = json.loads(file_info_data)
            if file_info.get('type') != MSG_FILE_DATA:
                self.log("收到无效的文件信息")
                return

            file_data = file_info.get('data', {})
            filename = file_data.get('name', filename)
            file_size = file_data.get('size', 0)

            # 发送确认
            confirm = {
                'type': MSG_SUCCESS,
                'data': "准备接收文件内容"
            }
            client_socket.sendall(json.dumps(confirm).encode('utf-8'))

            # 接收文件内容
            received_size = 0
            with open(file_path, 'wb') as f:
                while received_size < file_size:
                    data = client_socket.recv(FILE_CHUNK_SIZE)
                    if not data:
                        break
                    f.write(data)
                    received_size += len(data)

            if received_size == file_size:
                self.log(f"文件 {filename} 接收成功")
                confirm = {
                    'type': MSG_SUCCESS,
                    'data': "文件接收成功"
                }
                client_socket.sendall(json.dumps(confirm).encode('utf-8'))
            else:
                self.log(f"文件 {filename} 接收不完整，预期 {file_size} 字节，实际接收 {received_size} 字节")
                # 删除不完整的文件
                if os.path.exists(file_path):
                    os.remove(file_path)
                confirm = {
                    'type': MSG_ERROR,
                    'data': "文件接收不完整"
                }
                client_socket.sendall(json.dumps(confirm).encode('utf-8'))

        except Exception as e:
            self.log(f"接收文件错误: {str(e)}")
            response = {
                'type': MSG_ERROR,
                'data': str(e)
            }
            client_socket.sendall(json.dumps(response).encode('utf-8'))

    def start_discovery(self):
        """启动设备发现功能"""
        # 启动广播线程
        threading.Thread(target=self.broadcast_discovery, daemon=True).start()

        # 启动监听线程
        threading.Thread(target=self.listen_discovery, daemon=True).start()

        # 启动设备清理线程
        threading.Thread(target=self.cleanup_devices, daemon=True).start()

    def broadcast_discovery(self):
        """广播发现请求"""
        try:
            broadcast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            broadcast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
            broadcast_socket.settimeout(1)

            # 获取主机名
            hostname = socket.gethostname()

            # 广播消息
            while True:
                if self.server_running:
                    message = json.dumps({
                        'type': MSG_DISCOVER,
                        'data': {
                            'name': hostname,
                            'port': self.transfer_port
                        }
                    })

                    # 向局域网广播
                    broadcast_socket.sendto(
                        message.encode('utf-8'),
                        ('255.255.255.255', DISCOVERY_PORT)
                    )

                # 等待一段时间再广播
                time.sleep(BROADCAST_INTERVAL)

        except Exception as e:
            self.log(f"设备发现广播错误: {str(e)}")

    def listen_discovery(self):
        """监听设备发现请求和响应"""
        try:
            listen_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            listen_socket.bind(('0.0.0.0', DISCOVERY_PORT))
            listen_socket.settimeout(1)

            hostname = socket.gethostname()

            while True:
                try:
                    data, addr = listen_socket.recvfrom(BUFFER_SIZE)
                    message = json.loads(data.decode('utf-8'))
                    msg_type = message.get('type')
                    sender_ip = addr[0]

                    # 如果是发现请求，发送响应
                    if msg_type == MSG_DISCOVER:
                        # 不响应自己的广播
                        if sender_ip != self.get_local_ip():
                            response = json.dumps({
                                'type': MSG_RESPONSE,
                                'data': {
                                    'name': hostname,
                                    'port': self.transfer_port
                                }
                            })
                            listen_socket.sendto(response.encode('utf-8'), (sender_ip, DISCOVERY_PORT))

                    # 如果是响应消息，添加到设备列表
                    elif msg_type == MSG_RESPONSE:
                        data = message.get('data', {})
                        device_name = data.get('name', '未知设备')
                        device_port = data.get('port', DEFAULT_PORT)

                        self.devices[sender_ip] = {
                            'name': device_name,
                            'port': device_port,
                            'last_seen': datetime.now()
                        }

                        # 更新设备列表UI
                        self.update_device_list()

                except socket.timeout:
                    continue
                except Exception as e:
                    self.log(f"设备发现监听错误: {str(e)}")

        except Exception as e:
            self.log(f"设备发现监听启动错误: {str(e)}")

    def get_local_ip(self):
        """获取本地IP地址"""
        try:
            # 创建一个临时socket连接来获取本地IP
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(('8.8.8.8', 80))
            local_ip = s.getsockname()[0]
            s.close()
            return local_ip
        except:
            return '127.0.0.1'

    def update_device_list(self):
        """更新设备列表UI"""
        # 清空现有列表
        for item in self.device_listbox.get_children():
            self.device_listbox.delete(item)

        # 添加设备
        for ip, info in self.devices.items():
            last_seen = info['last_seen'].strftime('%Y-%m-%d %H:%M:%S')
            self.device_listbox.insert('', END, values=(info['name'], ip, last_seen))

    def refresh_devices(self):
        """刷新设备列表"""
        self.log("正在刷新设备列表...")
        self.update_device_list()

    def cleanup_devices(self):
        """清理长时间未响应的设备"""
        while True:
            current_time = datetime.now()
            devices_to_remove = []

            # 检查设备最后在线时间
            for ip, info in self.devices.items():
                # 如果超过30秒未更新，视为离线
                if (current_time - info['last_seen']).total_seconds() > 30:
                    devices_to_remove.append(ip)

            # 移除离线设备
            for ip in devices_to_remove:
                del self.devices[ip]
                self.log(f"设备 {ip} 已离线")

            # 更新设备列表
            self.update_device_list()

            # 等待一段时间再检查
            time.sleep(10)

    def on_device_select(self, event):
        """处理设备选择事件"""
        selected_items = self.device_listbox.selection()
        if selected_items:
            item = selected_items[0]
            values = self.device_listbox.item(item, 'values')
            if values:
                device_name, device_ip, _ = values
                device_port = self.devices.get(device_ip, {}).get('port', DEFAULT_PORT)

                self.connected_device = {
                    'name': device_name,
                    'ip': device_ip,
                    'port': device_port
                }

                self.log(f"已选择设备: {device_name} ({device_ip}:{device_port})")

                # 获取文件列表
                self.get_remote_file_list()

                # 更新按钮状态
                self.upload_btn.config(state=NORMAL)
            else:
                self.connected_device = None
                self.upload_btn.config(state=DISABLED)
        else:
            self.connected_device = None
            self.upload_btn.config(state=DISABLED)

    def get_remote_file_list(self):
        """获取远程设备的文件列表"""
        if not self.connected_device:
            return

        threading.Thread(target=self._get_remote_file_list_thread, daemon=True).start()

    def _get_remote_file_list_thread(self):
        """获取远程文件列表的线程函数"""
        try:
            self.log(f"正在获取 {self.connected_device['name']} 的文件列表...")

            # 连接到远程设备
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.connect((self.connected_device['ip'], self.connected_device['port']))

                # 发送文件列表请求
                request = {
                    'type': MSG_FILE_LIST,
                    'data': ''
                }
                s.sendall(json.dumps(request).encode('utf-8'))

                # 接收响应
                response_data = s.recv(BUFFER_SIZE).decode('utf-8')
                response = json.loads(response_data)

                if response.get('type') == MSG_FILE_LIST:
                    file_list = response.get('data', [])
                    self.update_file_list(file_list)
                    self.log(f"成功获取 {self.connected_device['name']} 的文件列表，共 {len(file_list)} 个文件")
                else:
                    self.log(f"获取文件列表失败: {response.get('data', '未知错误')}")

        except Exception as e:
            self.log(f"获取远程文件列表错误: {str(e)}")

    def update_file_list(self, file_list):
        """更新文件列表UI"""
        # 清空现有列表
        for item in self.file_listbox.get_children():
            self.file_listbox.delete(item)

        # 添加文件
        for file_info in file_list:
            self.file_listbox.insert('', END, values=(
                file_info['name'],
                file_info['size'],
                file_info['modified']
            ))

        # 禁用文件操作按钮
        self.download_btn.config(state=DISABLED)
        self.preview_btn.config(state=DISABLED)

    def on_file_select(self, event):
        """处理文件选择事件"""
        selected_items = self.file_listbox.selection()
        if selected_items and len(selected_items) == 1:
            # 启用文件操作按钮
            self.download_btn.config(state=NORMAL)
            self.preview_btn.config(state=NORMAL)
        else:
            # 禁用文件操作按钮
            self.download_btn.config(state=DISABLED)
            self.preview_btn.config(state=DISABLED)

    def download_file(self):
        """下载选中的文件"""
        if not self.connected_device:
            messagebox.showwarning("警告", "请先选择一个设备")
            return

        selected_items = self.file_listbox.selection()
        if not selected_items:
            messagebox.showwarning("警告", "请先选择一个文件")
            return

        item = selected_items[0]
        filename = self.file_listbox.item(item, 'values')[0]

        self.log(f"正在下载文件: {filename}")
        threading.Thread(target=self._download_file_thread, args=(filename,), daemon=True).start()

    def _download_file_thread(self, filename):
        """下载文件的线程函数"""
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.connect((self.connected_device['ip'], self.connected_device['port']))

                # 发送下载请求
                request = {
                    'type': MSG_DOWNLOAD,
                    'data': filename
                }
                s.sendall(json.dumps(request).encode('utf-8'))

                # 接收文件信息
                file_info_data = s.recv(BUFFER_SIZE).decode('utf-8')
                if not file_info_data:
                    self.log("未收到文件信息")
                    return

                file_info = json.loads(file_info_data)

                if file_info.get('type') == MSG_ERROR:
                    self.log(f"下载失败: {file_info.get('data', '未知错误')}")
                    return

                if file_info.get('type') != MSG_FILE_DATA:
                    self.log("收到无效的文件信息")
                    return

                file_data = file_info.get('data', {})
                filename = file_data.get('name', filename)
                file_size = file_data.get('size', 0)

                # 确认接收
                confirm = {
                    'type': MSG_SUCCESS,
                    'data': "准备接收文件内容"
                }
                s.sendall(json.dumps(confirm).encode('utf-8'))

                # 接收文件内容
                save_path = os.path.join(self.shared_folder, filename)
                received_size = 0

                with open(save_path, 'wb') as f:
                    while received_size < file_size:
                        data = s.recv(FILE_CHUNK_SIZE)
                        if not data:
                            break
                        f.write(data)
                        received_size += len(data)

                        # 更新进度（每接收10%更新一次）
                        if received_size % (file_size // 10) == 0 or received_size == file_size:
                            progress = int(received_size / file_size * 100)
                            self.log(f"下载 {filename}: {progress}%")

                if received_size == file_size:
                    self.log(f"文件 {filename} 下载成功，保存至: {save_path}")
                    confirm = {
                        'type': MSG_SUCCESS,
                        'data': "文件接收成功"
                    }
                    s.sendall(json.dumps(confirm).encode('utf-8'))
                else:
                    self.log(f"文件 {filename} 下载不完整，预期 {file_size} 字节，实际接收 {received_size} 字节")
                    # 删除不完整的文件
                    if os.path.exists(save_path):
                        os.remove(save_path)

        except Exception as e:
            self.log(f"下载文件错误: {str(e)}")

    def preview_file(self):
        """预览选中的文件（先下载到临时目录再打开）"""
        if not self.connected_device:
            messagebox.showwarning("警告", "请先选择一个设备")
            return

        selected_items = self.file_listbox.selection()
        if not selected_items:
            messagebox.showwarning("警告", "请先选择一个文件")
            return

        item = selected_items[0]
        filename = self.file_listbox.item(item, 'values')[0]

        self.log(f"正在预览文件: {filename}")
        threading.Thread(target=self._preview_file_thread, args=(filename,), daemon=True).start()

    def _preview_file_thread(self, filename):
        """预览文件的线程函数"""
        try:
            # 创建临时目录
            temp_dir = os.path.join(self.shared_folder, "临时预览")
            if not os.path.exists(temp_dir):
                os.makedirs(temp_dir)

            temp_path = os.path.join(temp_dir, filename)

            # 下载文件到临时目录
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.connect((self.connected_device['ip'], self.connected_device['port']))

                # 发送下载请求
                request = {
                    'type': MSG_DOWNLOAD,
                    'data': filename
                }
                s.sendall(json.dumps(request).encode('utf-8'))

                # 接收文件信息
                file_info_data = s.recv(BUFFER_SIZE).decode('utf-8')
                if not file_info_data:
                    self.log("未收到文件信息")
                    return

                file_info = json.loads(file_info_data)

                if file_info.get('type') == MSG_ERROR:
                    self.log(f"预览失败: {file_info.get('data', '未知错误')}")
                    return

                if file_info.get('type') != MSG_FILE_DATA:
                    self.log("收到无效的文件信息")
                    return

                file_data = file_info.get('data', {})
                filename = file_data.get('name', filename)
                file_size = file_data.get('size', 0)

                # 确认接收
                confirm = {
                    'type': MSG_SUCCESS,
                    'data': "准备接收文件内容"
                }
                s.sendall(json.dumps(confirm).encode('utf-8'))

                # 接收文件内容
                received_size = 0

                with open(temp_path, 'wb') as f:
                    while received_size < file_size:
                        data = s.recv(FILE_CHUNK_SIZE)
                        if not data:
                            break
                        f.write(data)
                        received_size += len(data)

                if received_size == file_size:
                    self.log(f"文件 {filename} 已准备好预览")
                    confirm = {
                        'type': MSG_SUCCESS,
                        'data': "文件接收成功"
                    }
                    s.sendall(json.dumps(confirm).encode('utf-8'))

                    # 使用系统默认程序打开文件
                    self.open_file_with_default_app(temp_path)
                else:
                    self.log(f"文件 {filename} 下载失败，无法预览")
                    # 删除不完整的文件
                    if os.path.exists(temp_path):
                        os.remove(temp_path)

        except Exception as e:
            self.log(f"预览文件错误: {str(e)}")

    def open_file_with_default_app(self, file_path):
        """使用系统默认程序打开文件"""
        try:
            if platform.system() == 'Windows':
                os.startfile(file_path)
            elif platform.system() == 'Darwin':  # macOS
                subprocess.run(['open', file_path])
            else:  # Linux
                subprocess.run(['xdg-open', file_path])

            self.log(f"已使用系统默认程序打开文件: {os.path.basename(file_path)}")
        except Exception as e:
            self.log(f"打开文件失败: {str(e)}")
            messagebox.showerror("错误", f"无法打开文件: {str(e)}")

    def add_local_files(self):
        """添加本地文件到上传列表"""
        files = filedialog.askopenfilenames(title="选择要上传的文件")
        if files:
            for file in files:
                # 只添加不存在的文件
                if file not in self.local_file_listbox.get(0, END):
                    self.local_file_listbox.insert(END, file)

    def remove_local_file(self):
        """从上传列表中移除选中的文件"""
        selected_indices = self.local_file_listbox.curselection()
        if selected_indices:
            # 从后往前删除，避免索引变化问题
            for i in sorted(selected_indices, reverse=True):
                self.local_file_listbox.delete(i)

    def upload_files(self):
        """上传选中的文件"""
        if not self.connected_device:
            messagebox.showwarning("警告", "请先选择一个设备")
            return

        selected_indices = self.local_file_listbox.curselection()
        if not selected_indices:
            messagebox.showwarning("警告", "请先选择要上传的文件")
            return

        # 获取选中的文件
        files_to_upload = [self.local_file_listbox.get(i) for i in selected_indices]

        # 逐个上传文件
        for file_path in files_to_upload:
            self.log(f"正在上传文件: {os.path.basename(file_path)}")
            threading.Thread(
                target=self._upload_file_thread,
                args=(file_path,),
                daemon=True
            ).start()
            # 等待一下再上传下一个文件
            time.sleep(1)

    def _upload_file_thread(self, file_path):
        """上传文件的线程函数"""
        try:
            filename = os.path.basename(file_path)

            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
                s.connect((self.connected_device['ip'], self.connected_device['port']))

                # 发送上传请求
                request = {
                    'type': MSG_UPLOAD,
                    'data': filename
                }
                s.sendall(json.dumps(request).encode('utf-8'))

                # 等待确认
                confirm = s.recv(BUFFER_SIZE).decode('utf-8')
                if not confirm or json.loads(confirm).get('type') != MSG_SUCCESS:
                    self.log("服务器未确认接收文件")
                    return

                # 发送文件信息
                file_size = os.path.getsize(file_path)
                file_info = {
                    'type': MSG_FILE_DATA,
                    'data': {
                        'name': filename,
                        'size': file_size
                    }
                }
                s.sendall(json.dumps(file_info).encode('utf-8'))

                # 等待确认
                confirm = s.recv(BUFFER_SIZE).decode('utf-8')
                if not confirm or json.loads(confirm).get('type') != MSG_SUCCESS:
                    self.log("服务器未准备好接收文件内容")
                    return

                # 发送文件内容
                sent_size = 0
                with open(file_path, 'rb') as f:
                    while sent_size < file_size:
                        data = f.read(FILE_CHUNK_SIZE)
                        if not data:
                            break
                        s.sendall(data)
                        sent_size += len(data)

                        # 更新进度（每发送10%更新一次）
                        if sent_size % (file_size // 10) == 0 or sent_size == file_size:
                            progress = int(sent_size / file_size * 100)
                            self.log(f"上传 {filename}: {progress}%")

                # 等待服务器确认
                confirm = s.recv(BUFFER_SIZE).decode('utf-8')
                if confirm and json.loads(confirm).get('type') == MSG_SUCCESS:
                    self.log(f"文件 {filename} 上传成功")
                else:
                    self.log(f"文件 {filename} 上传失败")

        except Exception as e:
            self.log(f"上传文件错误: {str(e)}")

    def on_close(self):
        """窗口关闭时的处理"""
        # 停止服务
        if self.server_running:
            self.stop_server()

        # 关闭窗口
        self.destroy()

    def create_widgets(self):
        pass
